{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Faiss"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Faiss是一个用于高效相似性搜索和密集向量聚类的库，由 Meta 公司的基础人工智能研究小组开发。\n",
    "- 它的算法支持在任意大小的向量集合中进行搜索，甚至可以处理无法放入内存的向量集合。\n",
    "- 它还包含了用于评估和参数调整的支持代码。\n",
    "- Faiss使用C++编写，并提供完整的Python/numpy封装，其中一些最常用的算法已经在GPU上实现。\n",
    "- 并且，Faiss 提供了 CPU 和 GPU 两种版本的安装包。\n",
    "\n",
    "Faiss GPU 版本：\n",
    "- 支持CPU 或 GPU 的输入。\n",
    "- GPU索引（indexes）可以即插即用替代CPU索引（例如，用GpuIndexFlatL2替换IndexFlatL2），并且对于GPU\n",
    "\n",
    "内存的复制操作会自动处理。\n",
    "- 如果输入和输出都保留在GPU上，则查询性能更好。\n",
    "- 支持单个和多个GPU的混合使用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "# Uncomment the following line if you need to initialize FAISS with no AVX2 optimization\n",
    "# os.environ['FAISS_NO_AVX2'] = '1'\n",
    "\n",
    "from langchain_community.document_loaders import TextLoader\n",
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_openai import OpenAIEmbeddings\n",
    "from langchain_text_splitters import CharacterTextSplitter\n",
    "\n",
    "loader = TextLoader(\"../data/city.txt\")\n",
    "documents = loader.load()\n",
    "text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)\n",
    "docs = text_splitter.split_documents(documents)\n",
    "embeddings = OpenAIEmbeddings()\n",
    "db = FAISS.from_documents(docs, embeddings)\n",
    "print(db.index.ntotal)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Querying"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Document(page_content='河北省的省会是石家庄\\n山东省的省会是济南\\n辽宁省的省会是沈阳\\n湖北省的省会是武汉', metadata={'source': '../data/city.txt'})]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "query = \"What did the president say about Ketanji Brown Jackson\"\n",
    "docs = db.similarity_search(query)\n",
    "docs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## As a Retriever"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Document(page_content='河北省的省会是石家庄\\n山东省的省会是济南\\n辽宁省的省会是沈阳\\n湖北省的省会是武汉', metadata={'source': '../data/city.txt'})]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "retriever = db.as_retriever()\n",
    "docs = retriever.invoke(query)\n",
    "docs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Similarity Search with score"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(Document(page_content='河北省的省会是石家庄\\n山东省的省会是济南\\n辽宁省的省会是沈阳\\n湖北省的省会是武汉', metadata={'source': '../data/city.txt'}),\n",
       "  0.67412794)]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "docs_and_scores = db.similarity_search_with_score(query)\n",
    "docs_and_scores"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Document(page_content='河北省的省会是石家庄\\n山东省的省会是济南\\n辽宁省的省会是沈阳\\n湖北省的省会是武汉', metadata={'source': '../data/city.txt'})]"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "embedding_vector = embeddings.embed_query(query)\n",
    "docs_and_scores = db.similarity_search_by_vector(embedding_vector)\n",
    "docs_and_scores"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Serializing and De-Serializing to bytes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings\n",
    "\n",
    "pkl = db.serialize_to_bytes()  # serializes the faiss\n",
    "embeddings = HuggingFaceEmbeddings(model_name=\"all-MiniLM-L6-v2\")\n",
    "\n",
    "db = FAISS.deserialize_from_bytes(\n",
    "    embeddings=embeddings, serialized=pkl\n",
    ")  # Load the index"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Merging"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'b73d0a59-b47e-4f87-b2bd-e3dad43f2178': Document(page_content='河北省的省会是石家庄\\n山东省的省会是济南\\n辽宁省的省会是沈阳\\n湖北省的省会是武汉', metadata={'source': '../data/city.txt'})}"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "db.docstore._dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'af57c55c-f12e-4570-85d7-49fdb86ba94c': Document(page_content='foo')}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "db1 = FAISS.from_texts([\"foo\"], embeddings)\n",
    "db2 = FAISS.from_texts([\"bar\"], embeddings)\n",
    "\n",
    "db1.docstore._dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'f0f787ee-f5ef-4761-b3aa-0bfa5ee311d7': Document(page_content='bar')}"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "db2.docstore._dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "db1.merge_from(db2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'af57c55c-f12e-4570-85d7-49fdb86ba94c': Document(page_content='foo'),\n",
       " 'f0f787ee-f5ef-4761-b3aa-0bfa5ee311d7': Document(page_content='bar')}"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "db1.docstore._dict"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Similarity Search with filtering"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Content: foo, Metadata: {'page': 1}, Score: 0.0\n",
      "Content: foo, Metadata: {'page': 2}, Score: 0.0\n",
      "Content: foo, Metadata: {'page': 3}, Score: 0.0\n",
      "Content: foo, Metadata: {'page': 4}, Score: 0.0\n",
      "Content: bar, Metadata: {'page': 1}, Score: 0.3147187829017639\n",
      "Content: barbar, Metadata: {'page': 2}, Score: 0.31650424003601074\n",
      "Content: bar bruh, Metadata: {'page': 4}, Score: 0.36809641122817993\n",
      "Content: bar burr, Metadata: {'page': 3}, Score: 0.39583224058151245\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.documents import Document\n",
    "\n",
    "list_of_documents = [\n",
    "    Document(page_content=\"foo\", metadata=dict(page=1)),\n",
    "    Document(page_content=\"bar\", metadata=dict(page=1)),\n",
    "    Document(page_content=\"foo\", metadata=dict(page=2)),\n",
    "    Document(page_content=\"barbar\", metadata=dict(page=2)),\n",
    "    Document(page_content=\"foo\", metadata=dict(page=3)),\n",
    "    Document(page_content=\"bar burr\", metadata=dict(page=3)),\n",
    "    Document(page_content=\"foo\", metadata=dict(page=4)),\n",
    "    Document(page_content=\"bar bruh\", metadata=dict(page=4)),\n",
    "]\n",
    "db = FAISS.from_documents(list_of_documents, embeddings)\n",
    "results_with_scores = db.similarity_search_with_score(\"foo\",k=20)\n",
    "for doc, score in results_with_scores:\n",
    "    print(f\"Content: {doc.page_content}, Metadata: {doc.metadata}, Score: {score}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Content: foo, Metadata: {'page': 1}, Score: 0.0\n",
      "Content: bar, Metadata: {'page': 1}, Score: 0.3147187829017639\n"
     ]
    }
   ],
   "source": [
    "results_with_scores = db.similarity_search_with_score(\"foo\", filter=dict(page=1))\n",
    "# Or with a callable:\n",
    "# results_with_scores = db.similarity_search_with_score(\"foo\", filter=lambda d: d[\"page\"] == 1)\n",
    "for doc, score in results_with_scores:\n",
    "    print(f\"Content: {doc.page_content}, Metadata: {doc.metadata}, Score: {score}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Saving and loading"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Document(page_content='河北省的省会是石家庄\\n山东省的省会是济南\\n辽宁省的省会是沈阳\\n湖北省的省会是武汉', metadata={'source': '../data/city.txt'})]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "db.save_local(\"faiss_index\")\n",
    "\n",
    "new_db = FAISS.load_local(\"faiss_index\", embeddings,allow_dangerous_deserialization=True)\n",
    "\n",
    "docs = new_db.similarity_search(query)\n",
    "docs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Delete"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "count before: 1\n",
      "count after: 0\n"
     ]
    }
   ],
   "source": [
    "print(\"count before:\", db.index.ntotal)\n",
    "db.delete([db.index_to_docstore_id[0]])\n",
    "print(\"count after:\", db.index.ntotal)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain0_1",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
